/*
 * scalarvolume.cpp
 *
 * Copyright 2012 The University of Auckland
 *
 *  Created on: 01/10/2012
 *      Author: cvan680
 */

#include "scalarvolume.h"
#include "voreen/core/datastructures/volume/volumefactory.h"
#include "voreen/core/datastructures/volume/volumeatomic.h"
#include "voreen/core/datastructures/volume/volumeram.h"
#include "voreen/core/datastructures/volume/volume.h"
#include "tgt/textureunit.h"
#include "tgt/filesystem.h"

namespace voreen {

ScalarVolume::ScalarVolume()
	: VolumeProcessor()
	, outport(Port::OUTPORT, "volume", 0)
	, prop_xdim("dimension_x", "Dimension (x)", 128, 1, 1024)
	, prop_ydim("dimension_y", "Dimension (y)", 128, 1, 1024)
	, prop_zdim("dimension_z", "Dimension (z)", 2, 1, 1024)
, prop_sing1Type(0)
, prop_sing1_x("sing1_x", "Singularity 1 location x", 50, 0, 100)
, prop_sing1_y("sing1_y", "Singularity 1 location y", 50, 0, 100)
, prop_sing2Type(0)
, prop_sing2_x("sing2_x", "Singularity 2 location x", 50, 0, 100)
, prop_sing2_y("sing2_y", "Singularity 2 location y", 50, 0, 100)
, prop_sing3Type(0)
, prop_sing3_x("sing3_x", "Singularity 3 location x", 50, 0, 100)
, prop_sing3_y("sing3_y", "Singularity 3 location y", 50, 0, 100)
, prop_sing4Type(0)
, prop_sing4_x("sing4_x", "Singularity 4 location x", 50, 0, 100)
, prop_sing4_y("sing4_y", "Singularity 4 location y", 50, 0, 100)
, prop_sing5Type(0)
, prop_sing5_x("sing5_x", "Singularity 5 location x", 50, 0, 100)
, prop_sing5_y("sing5_y", "Singularity 5 location y", 50, 0, 100)
, prop_sing6Type(0)
, prop_sing6_x("sing6_x", "Singularity 6 location x", 50, 0, 100)
, prop_sing6_y("sing6_y", "Singularity 6 location y", 50, 0, 100)
	, update_prop("update", "Update outputs")
{
    addPort(outport);

    addProperty(prop_xdim);
    addProperty(prop_ydim);
    addProperty(prop_zdim);

    prop_sing1Type = new OptionProperty<SingularityType>("singularityType1", "Singularity 1: ");
    prop_sing1Type->addOption("none", "----", NONE);
    prop_sing1Type->addOption("node", "node", NODE);
    prop_sing1Type->addOption("saddle", "saddle", SADDLE);
    prop_sing1Type->addOption("center", "center", CENTER);
    prop_sing1Type->addOption("attr_focus", "attr_focus", ATTRACTING_FOCUS);
    prop_sing1Type->addOption("rep_focus", "rep_focus", REPELLING_FOCUS);
	addProperty(prop_sing1Type);
	addProperty(prop_sing1_x);
	addProperty(prop_sing1_y);

    prop_sing2Type = new OptionProperty<SingularityType>("singularityType2", "Singularity 2: ");
    prop_sing2Type->addOption("none", "----", NONE);
    prop_sing2Type->addOption("node", "node", NODE);
    prop_sing2Type->addOption("saddle", "saddle", SADDLE);
    prop_sing2Type->addOption("center", "center", CENTER);
    prop_sing2Type->addOption("attr_focus", "attr_focus", ATTRACTING_FOCUS);
    prop_sing2Type->addOption("rep_focus", "rep_focus", REPELLING_FOCUS);
	addProperty(prop_sing2Type);
	addProperty(prop_sing2_x);
	addProperty(prop_sing2_y);

    prop_sing3Type = new OptionProperty<SingularityType>("singularityType3", "Singularity 3: ");
    prop_sing3Type->addOption("none", "----", NONE);
    prop_sing3Type->addOption("node", "node", NODE);
    prop_sing3Type->addOption("saddle", "saddle", SADDLE);
    prop_sing3Type->addOption("center", "center", CENTER);
    prop_sing3Type->addOption("attr_focus", "attr_focus", ATTRACTING_FOCUS);
    prop_sing3Type->addOption("rep_focus", "rep_focus", REPELLING_FOCUS);
	addProperty(prop_sing3Type);
	addProperty(prop_sing3_x);
	addProperty(prop_sing3_y);

    prop_sing4Type = new OptionProperty<SingularityType>("singularityType4", "Singularity 4: ");
    prop_sing4Type->addOption("none", "----", NONE);
    prop_sing4Type->addOption("node", "node", NODE);
    prop_sing4Type->addOption("saddle", "saddle", SADDLE);
    prop_sing4Type->addOption("center", "center", CENTER);
    prop_sing4Type->addOption("attr_focus", "attr_focus", ATTRACTING_FOCUS);
    prop_sing4Type->addOption("rep_focus", "rep_focus", REPELLING_FOCUS);
	addProperty(prop_sing4Type);
	addProperty(prop_sing4_x);
	addProperty(prop_sing4_y);

    prop_sing5Type = new OptionProperty<SingularityType>("singularityType5", "Singularity 5: ");
    prop_sing5Type->addOption("none", "----", NONE);
    prop_sing5Type->addOption("node", "node", NODE);
    prop_sing5Type->addOption("saddle", "saddle", SADDLE);
    prop_sing5Type->addOption("center", "center", CENTER);
    prop_sing5Type->addOption("attr_focus", "attr_focus", ATTRACTING_FOCUS);
    prop_sing5Type->addOption("rep_focus", "rep_focus", REPELLING_FOCUS);
	addProperty(prop_sing5Type);
	addProperty(prop_sing5_x);
	addProperty(prop_sing5_y);

    prop_sing6Type = new OptionProperty<SingularityType>("singularityType6", "Singularity 6: ");
    prop_sing6Type->addOption("none", "----", NONE);
    prop_sing6Type->addOption("node", "node", NODE);
    prop_sing6Type->addOption("saddle", "saddle", SADDLE);
    prop_sing6Type->addOption("center", "center", CENTER);
    prop_sing6Type->addOption("attr_focus", "attr_focus", ATTRACTING_FOCUS);
    prop_sing6Type->addOption("rep_focus", "rep_focus", REPELLING_FOCUS);
	addProperty(prop_sing6Type);
	addProperty(prop_sing6_x);
	addProperty(prop_sing6_y);

	update_prop.onClick(CallMemberAction<ScalarVolume>(this, &ScalarVolume::update));
	addProperty(update_prop);
}


Processor* ScalarVolume::create() const {
    return new ScalarVolume();
}

/**
 * What to do here? Now we do everything through the update button.
 * We don't want to create new volumes everytime we change any parameter,
 * so just leave this empty and only update when user presses button.
 */
void ScalarVolume::process() {

    // check for OpenGL errors
    LGL_ERROR;
}


void ScalarVolume::initialize() throw (VoreenException) {
    // call superclass function first
	VolumeProcessor::initialize();
	volumefactory = new VolumeFactory();
}

void ScalarVolume::deinitialize() throw (VoreenException) {
    // call superclass function last
	VolumeProcessor::deinitialize();
}


void ScalarVolume::update() {
	// TODO create volume by type
	volume_ = generateScalarVolume();
	outport.setData(volume_, true);
}


Volume* ScalarVolume::generateScalarVolume(){

	tgt::ivec3 dims = getDimensions();

	VolumeRAM* scalarVR = volumefactory->create("uint8", dims);
	float val = 0.0;

	// get the shortest dimension and divide by two
	float minimum = std::min((dims.x/2), (dims.y/2));

	// calculate distances to inner and outter sphere
	float dist_closer = 0.5 * minimum;
	float dist_further = 0.85 * minimum;

	// calculate center of the volume
	float x_center = dims.x / 2;
	float y_center = dims.y / 2;
	float z_center = 1;

	// calculate middle between the two spheres
	float cutoff = dist_closer + ((dist_further - dist_closer) / 2);

	float dist_to_center;
	float dist_to_circle;

	// calculate scalar value for each voxel in volume
	for(int x=0; x<dimension_x_prop.get(); x++){
		for(int y=0; y<dimension_y_prop.get(); y++){
			for(int z=0; z<dimension_z_prop.get(); z++){

				dist_to_center = sqrt( (x - x_center)*(x - x_center) + (y - y_center)*(y - y_center) + (z - z_center)*(z - z_center) );

				if(dist_to_center <= cutoff){
					// first (smaller) peak: max .5*255: 0.5 / (1 + x^2)
					dist_to_circle = abs(dist_closer - dist_to_center);
					val = 0.5 / (1.0 + (dist_to_circle * dist_to_circle));
				}else{
					// second (larger) peak: max 255: 1.0 / (1 + x^2)
					dist_to_circle = abs(dist_further - dist_to_center);
					val = 1.0 / (1.0 + (dist_to_circle * dist_to_circle));

					// small hack to ensure inner circle has a 'unique' value between 0.35 and 0.50
					if(val <= 0.51 && val >= 0.35){
						if(val <= 0.43){ val = 0.35; }else{ val = 0.51; }
					}
				}

				scalarVR->setVoxelNormalized(val, x, y, z);
			}
		}
	}

	Volume* result = new Volume(scalarVR, vec3(1.0, 1.0, 1.0), vec3(0.0, 0.0, 0.0));
	centerVolume(result);
	return result;
}


tgt::ivec3 ScalarVolume::getDimensions(){
	return tgt::ivec3(prop_xdim.get(), prop_ydim.get(), prop_zdim.get());
}

} // namespace
